 package com.svw.asmpcloud.handler;

import java.util.function.Function;
import java.util.function.Predicate;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import reactor.core.publisher.Mono;

/**
 * 自定义的鉴权服务，通过鉴权的才能继续访问某个请求<br>
 * 由于SpringGateWay基于WebFlux，所以SpringSecruity很多原有写法，都得改为WebFlux的方式才能生效！
 */
@Component
public class UriReactiveAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    @Autowired
    private AntPathMatcher antPathMatcher;
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext object) {
        ServerHttpRequest request = object.getExchange().getRequest();
        String uri = request.getPath().pathWithinApplication().value();

        return authentication
                // Lambda 写法可以很简介,但是不方便调试和查看代码细节,所以还是用传统的写法
                // 检查是否认证通过
                .filter(new Predicate<Authentication>() {
                    @Override
                    public boolean test(Authentication t) {
                        return t.isAuthenticated();
                    }
                })
                // 获取用户拥有的权限集合(这里是uris)
                .flatMapIterable(new Function<Authentication, Iterable<? extends GrantedAuthority>>() {
                    @Override
                    public Iterable<? extends GrantedAuthority> apply(Authentication t) {
                        return t.getAuthorities();
                    }

                })
                // 这里放回用户拥有的权限代码(uri)会用于下面的的匹配,
                // 如果该用户有10个权限代码,那么apply(GrantedAuthority t)会被调用10次
                .map(new Function<GrantedAuthority, String>() {
                    @Override
                    public String apply(GrantedAuthority t) {
                        return t.getAuthority();
                    }
                })
                // 进行匹配,如果该用户有10个权限代码,那么apply(GrantedAuthority t)会被调用10次,直到匹配上为止
                .any(new Predicate<String>() {
                    @Override
                    public boolean test(String pattern) {
                        if (antPathMatcher.match(pattern, uri)) {
                            return true;
                        }
                        return false;
                    }
                }).map(new Function<Boolean, AuthorizationDecision>() {
                    @Override
                    public AuthorizationDecision apply(Boolean t) {
                        return new AuthorizationDecision(t);
                    }

                }).defaultIfEmpty(new AuthorizationDecision(false));
    }

    @Override
    public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
        return null;
    }

}
